#pragma once

#include "DatabaseConnection.h"
#include <list>

struct RawEntityFeed
{
    ResultCol<Type_UINTEGER>        id;
    ResultCol<Type_STRING, 10>      name;
    ResultCol<Type_STRING, 255>     description;
    ResultCol<Type_STRING, 10>      pnacprefix;
    ResultCol<Type_STRING, 10>      attributioin;
    ResultCol<Type_INTEGER>         alertstatus;
    ResultCol<Type_STRING, 300>     emaillist;
};

struct RawEntityAlert
{
    ResultCol<Type_UINTEGER>        id;
    ResultCol<Type_UINTEGER>        feedRuleId;
    ResultCol<Type_TIMESTAMP, sizeof(tagTIMESTAMP_STRUCT) >  createTime;
    ResultCol<Type_STRING, 1000>    message;
    ResultCol<Type_INTEGER>         alertstatus;
    ResultCol<Type_INTEGER>         flag;
};

struct RawEntityRule
{
    ResultCol<Type_UINTEGER>        id;
    ResultCol<Type_STRING, 10>      name;
    ResultCol<Type_STRING, 500>     description;
    ResultCol<Type_STRING, 500>     format;
};

struct RawEntityFeedRule
{
    ResultCol<Type_UINTEGER>        id;
    ResultCol<Type_UINTEGER>        feedID;
    ResultCol<Type_UINTEGER>        ruleID;
    ResultCol<Type_STRING, 1000>          value;
    ResultCol<Type_INTEGER>         alertStatus;
};

struct RawEntityNews
{
    ResultCol<Type_UINTEGER>        id;
    ResultCol<Type_STRING, 100 >    guid;
    ResultCol<Type_STRING, 15 >     pnac; 
    //TODO find out a way to eliminate the length limitation
    //fully implement the news logic, and try to figure out how to 
    //generate the feed's profile
};
//This object deals with database operations
class DataBaseAccessObject
{
public:

    void QueryAlertList( std::vector<RawEntityAlert>& alerts )
    {
        RawEntityAlert alert;
        try
        {
            m_selectAlertStatement->Execute();
            
            alert.id.BindStatement( m_selectAlertStatement.get(), 1 );
            alert.feedRuleId.BindStatement( m_selectAlertStatement.get(), 2 );
            alert.message.BindStatement( m_selectAlertStatement.get(), 3 );
            alert.alertstatus.BindStatement( m_selectAlertStatement.get(), 4 );
            alert.flag.BindStatement( m_selectAlertStatement.get(), 5 );
            alert.createTime.BindStatement( m_selectAlertStatement.get(), 6 );

            while( m_selectAlertStatement->FetchNext() )
            {
                //We get one alert entity
                alerts.push_back( alert );
            }

            m_selectAlertStatement->FetchEnd();
        }
        catch( DataBaseException& e )
        {
            std::cout << "Failed to get alert list" << std::endl;
            std::cout << "DBError: " << e.what() << std::endl;
        }
    }

    void QueryAlertListAutoExpandTest()
    {
        struct TestEntity
        {
            ResultCol<Type_UINTEGER>        id;
            ResultCol<Type_UINTEGER>        feedRuleId;
            ResultCol<Type_TIMESTAMP, sizeof(tagTIMESTAMP_STRUCT) >  createTime;
            ResultCol<Type_STRING, 1>    message;
            ResultCol<Type_INTEGER>         alertstatus;
            ResultCol<Type_INTEGER>         flag;
        };

        TestEntity entity;

        try
        {
            m_selectAlertStatement->Execute();
            m_selectAlertStatement->BindColumn( 3, entity.message );

            while( m_selectAlertStatement->FetchNextEx() )
            {
                std::cout << "Fetch next" << std::endl;
            }

            m_selectAlertStatement->FetchEnd();
        }
        catch( DataBaseException& e )
        {
            std::cout << "QueryAlertListAutoExpandTest failed, " << e.what() << std::endl;
        }
    }

    void QueryFeedList( std::vector<RawEntityFeed>& feeds  )
    {
        RawEntityFeed rawFeed;

        try
        {
            //No parameter needed 
            //id, name, description, pnacPrefix, attributionCode, alertStatus, emailList
            m_selectFeedStatement->Execute();

            rawFeed.id.BindStatement( m_selectFeedStatement.get(), 1 );
            rawFeed.name.BindStatement( m_selectFeedStatement.get(), 2 );
            rawFeed.description.BindStatement( m_selectFeedStatement.get(), 3 );
            rawFeed.pnacprefix.BindStatement( m_selectFeedStatement.get(), 4 );
            rawFeed.attributioin.BindStatement( m_selectFeedStatement.get(), 5 );
            rawFeed.alertstatus.BindStatement( m_selectFeedStatement.get(), 6 );
            rawFeed.emaillist.BindStatement( m_selectFeedStatement.get(), 7 );

            while( m_selectFeedStatement->FetchNext() )
            {
                feeds.push_back( rawFeed );
            }

            m_selectFeedStatement->FetchEnd();
        }
        catch( DataBaseException& e )
        {
            std::cout << "Failed to get feed list" << std::endl;
            std::cout << "DBError: " << e.what() << std::endl;
        }
    }

    void QueryFeedRuleValidList( std::vector< RawEntityFeedRule >& feedRules )
    {
        RawEntityFeedRule rawFeedRule;
        try
        {
            rawFeedRule.id.BindStatement( m_selectFeedRuleStatement.get(), 1 );
            rawFeedRule.feedID.BindStatement( m_selectFeedRuleStatement.get(), 2 );
            rawFeedRule.ruleID.BindStatement( m_selectFeedRuleStatement.get(), 3 );
            rawFeedRule.value.BindStatement( m_selectFeedRuleStatement.get(), 4 );
            rawFeedRule.alertStatus.BindStatement( m_selectFeedRuleStatement.get(), 5 );

            m_selectFeedRuleStatement->Execute();

            while( m_selectFeedRuleStatement->FetchNext() )
            {
                feedRules.push_back( rawFeedRule );
            }

            m_selectFeedRuleStatement->FetchEnd();

        }
        catch( DataBaseException& e )
        {
            std::cout << "Failed to get feed rule list" << std::endl;
            std::cout << "DBError: " << e.what() << std::endl;
        }
    }



    void UpdateAlertFlag( RawEntityAlert* alert )
    {
        try
        {
            //Update the information based on its id
            StatementParam param1( Param_Input, SQL_C_ULONG, SQL_INTEGER ); 
            param1.SetValue<int>( alert->flag.GetValue<int>() );

            m_updateAlertStatement->BindParameter( 
                1, 
                &param1 );

            StatementParam param2( Param_Input, SQL_C_ULONG, SQL_INTEGER );
            param2.SetValue<int>( alert->id.GetValue<int>() );

            m_updateAlertStatement->BindParameter(
                2,
                &param2 );

            m_updateAlertStatement->Execute();

        }
        catch( DataBaseException& e )
        {
            std::cout << e.what() << std::endl;
        }
    }

    void UpdateFeedStatus( RawEntityFeed* feed )
    {
        try
        {
            StatementParam alertStatusParam( Param_Input, SQL_C_ULONG, SQL_INTEGER );
            alertStatusParam.SetValue<int>( feed->alertstatus.GetValue<int>() );
            m_updateFeedStatusStatement->BindParameter( 1, &alertStatusParam );

            StatementParam idParam( Param_Input, SQL_C_ULONG, SQL_INTEGER );
            idParam.SetValue<int>( feed->id.GetValue<int>() );
            m_updateFeedStatusStatement->BindParameter( 2, &idParam );

            m_updateFeedStatusStatement->Execute();

        }
        catch( DataBaseException& e )
        {
            std::cout << "Failed in Update feed status " << std::endl;
            std::cout << e.what() << std::endl;
        }
    }

    void UpdateFeedRuleStatus( RawEntityFeedRule* feedRule )
    {
        try
        {
            StatementParam alertStatusFRParam( Param_Input, SQL_C_ULONG, SQL_INTEGER );
            alertStatusFRParam.SetValue<int>( feedRule->alertStatus.GetValue<int>() );
            m_updateFeedRuleStatusStatement->BindParameter( 1, &alertStatusFRParam );

            StatementParam feedRuleId( Param_Input, SQL_C_ULONG, SQL_INTEGER );
            feedRuleId.SetValue<int>( feedRule->id.GetValue<int>() );
            m_updateFeedRuleStatusStatement->BindParameter( 2, &feedRuleId );

            m_updateFeedRuleStatusStatement->Execute();
        }
        catch( DataBaseException& e )
        {
            std::cout << "Failed in Update feedRule Status" << std::endl;
            std::cout << e.what() << std::endl;
        }
    }

    bool QueryFeedInformation( RawEntityFeed* feed )
    {
        //Query feed information based on its id
        try
        {
            StatementParam feedidParam( Param_Input, SQL_C_ULONG, SQL_INTEGER );
            feedidParam.SetValue<int>( feed->id.GetValue<int>() );
            m_queryFeedInformationStatement->BindParameter( 1, &feedidParam );

            m_queryFeedInformationStatement->Execute();

            //Bind the col  name, pnacPrefix, attributionCode, alertStatus, emailList
            feed->name.BindStatement( m_queryFeedInformationStatement.get(), 1 );
            feed->pnacprefix.BindStatement( m_queryFeedInformationStatement.get(), 2 );
            feed->attributioin.BindStatement( m_queryFeedInformationStatement.get(), 3 );
            feed->alertstatus.BindStatement( m_queryFeedInformationStatement.get(), 4 );
            feed->emaillist.BindStatement( m_queryFeedInformationStatement.get(), 5 );

            bool ret = m_queryFeedInformationStatement->FetchNext();
            m_queryFeedInformationStatement->FetchEnd();
            return ret;
        }
        catch( DataBaseException& e )
        {
            std::cout << "Failed to query feed information" << std::endl;
            std::cout << e.what() << std::endl;
            return false;
        }
    }

    DataBaseAccessObject( 
        const std::string& serverName, 
        const std::string& userName,
        const std::string& authentication )
        :m_serverName( serverName )
        ,m_userName( userName )
        ,m_authentication( authentication )
    {
        try
        {
            m_dbConnection.Initialize();
            //m_dbConnection.Connect( "NEWSM_COL2_SQLSERVER", "sa", "Tin.netS1" );
            m_dbConnection.Connect( m_serverName.c_str(),
                m_userName.c_str(),
                m_authentication.c_str() );

            m_selectAlertStatement = 
                m_dbConnection.CreateStatement(
                    L"select alert.id, alert.feedRuleID, alert.message, alert.alertStatus, alert.flag, alert.createTime \n"
                    L"from alert \n"
                    L"where ( flag <> 1 )"
                );

            m_selectAlertStatement->Prepare();

            m_selectFeedRuleStatement = 
                m_dbConnection.CreateStatement(
                    L"SELECT feedRules.id, feedRules.feedID, feedRules.ruleID, feedRules.value, feedRules.alertStatus \n"
                    L"FROM   feedRules INNER JOIN\n"
                    L" feed ON feedRules.feedID = feed.id \n"
                    L"WHERE  (feed.isEnabled = 1)"
                );
            m_selectFeedRuleStatement->Prepare();

            m_selectFeedStatement = 
                m_dbConnection.CreateStatement(
                    L"select id, name, description, pnacPrefix, attributionCode, alertStatus, emailList\n"
                    L"from feed \n" 
                    );
            m_selectFeedStatement->Prepare();

            m_updateAlertStatement = 
                m_dbConnection.CreateStatement(
                    L"update alert "
                    L"set flag = ? "
                    L"where ( id = ? ) "
                    );
            m_updateAlertStatement->Prepare();

            m_updateFeedStatusStatement = 
                m_dbConnection.CreateStatement(
                    L"UPDATE feed \n"
                    L"SET alertStatus = ? "
                    L"WHERE ( id = ? ) " );
            m_updateFeedStatusStatement->Prepare();    
            
            m_updateFeedRuleStatusStatement = 
                m_dbConnection.CreateStatement(
                    L"UPDATE feedRules "
                    L"SET alertStatus = ? "
                    L"WHERE ( id = ? ) " );
            m_updateFeedRuleStatusStatement->Prepare();

            m_queryFeedInformationStatement = 
                m_dbConnection.CreateStatement(
                    L"SELECT name, pnacPrefix, attributionCode, alertStatus, emailList \n"
                    L"FROM feed \n"
                    L"WHERE ( id = ? ) \n " );
            m_queryFeedInformationStatement->Prepare();
            
        }
        catch( DataBaseException& e )
        { 
            std::cout << e.what();
        }
    }

    ~DataBaseAccessObject()
    {
        m_dbConnection.DisConnect();
        m_dbConnection.UnInitialize();
    }
private:
    DataBaseConnection m_dbConnection;
    std::shared_ptr<DataBaseStatement>  m_selectAlertStatement;
    std::shared_ptr<DataBaseStatement>  m_selectFeedStatement;
    std::shared_ptr<DataBaseStatement>  m_selectFeedRuleStatement;

    std::shared_ptr<DataBaseStatement>  m_updateAlertStatement;
    std::shared_ptr<DataBaseStatement>  m_updateFeedStatusStatement;
    std::shared_ptr<DataBaseStatement>  m_updateFeedRuleStatusStatement;
    std::shared_ptr<DataBaseStatement>  m_queryFeedInformationStatement;

    std::string m_serverName;
    std::string m_userName;
    std::string m_authentication;
};